#include <stdinc.h>
#include "driver/ledc.h"
#include "soc/ledc_reg.h"
#include "soc/ledc_struct.h"
#include "pwm.h"

#define LOGTAG "SZW:PWM"


typedef struct {
	ledc_timer_t timer;
	ledc_channel_t channel;
	uint32_t total;
	uint32_t remain;
	FUNC_pwmIsrCallback callback;
	void *cookie;
	int gpio;
} pwm_info_t;

#define PWM_MAX 4

static pwm_info_t _PWM[PWM_MAX] = {
	{ LEDC_TIMER_0, LEDC_CHANNEL_4, 0, 0, NULL, NULL, GPIO_NUM_MAX },
	{ LEDC_TIMER_1, LEDC_CHANNEL_5, 0, 0, NULL, NULL, GPIO_NUM_MAX },
	{ LEDC_TIMER_2, LEDC_CHANNEL_6, 0, 0, NULL, NULL, GPIO_NUM_MAX },
	{ LEDC_TIMER_3, LEDC_CHANNEL_7, 0, 0, NULL, NULL, GPIO_NUM_MAX },
};

const pwm_handle_t PWM0 = &_PWM[0];
const pwm_handle_t PWM1 = &_PWM[1];
const pwm_handle_t PWM2 = &_PWM[2];
const pwm_handle_t PWM3 = &_PWM[3];

// 每个周期总共有多少个 PWM 波形 count
#define PWM_COUNT_PER_CYCLE(res)     		(1<<res)
// duty 时间有多少个 PWM 波形 count
#define PWM_COUNT_PER_DUTY(res, dutyRatio)  (PWM_COUNT_PER_CYCLE(res)*dutyRatio/100 + 1)

// 人的听觉范围: 20 ~ 19912 Hz
#define AUDIO_FREQ_MIN  20
#define AUDIO_FREQ_MAX  4000	// 实际到 4kHz 就基本听不到了

// 中断处理函数句柄
static intr_handle_t pwm_isr_handle = NULL;


/// 中断处理函数
static void IRAM_ATTR ledc_pwm_isr(void* dummy) {
	pwm_info_t *pwm;
	uint32_t int_st = LEDC.int_st.val;

	// 逐个检查是哪个定时器产生了中断
	for(int i=0; i<PWM_MAX; i++) {
		pwm = &_PWM[i];

		if(int_st & BIT(LEDC_HSTIMER0_OVF_INT_CLR_S + pwm->timer)) {
			// PWM 计数值减一
			if((pwm->remain > 0) && (pwm->remain != PWM_COUNT_FOREVER)) { pwm->remain--; }
			if(pwm->remain == 0) {
				// 如果 PWM 计数完成，则调用回调函数（如果有的话）
				if((pwm->callback)) {
					if(!pwm->callback(pwm->cookie)) {
						// 停止
						PWM_stop(pwm);
					}
				} else {
					// 停止
					PWM_stop(pwm);
				}
			}

			// 清除中断
			LEDC.int_clr.val |= BIT(LEDC_HSTIMER0_OVF_INT_CLR_S + pwm->timer);
		}
	}
}

/// 在指定频率下，获取最佳的 duty 分辨率
static ledc_timer_bit_t ledc_get_best_duty_resolution(pwm_info_t *pwm, uint32_t freq_hz) {
    uint32_t clock_divider;
    uint32_t precision;
    u64 div = ((uint64_t) LEDC_APB_CLK_HZ << 8) / freq_hz;

	for(uint32_t res=20; res>0; res--) {
	    precision = (0x1 << res);
	    clock_divider = div / precision;
	    if (clock_divider > 256 && clock_divider <= LEDC_DIV_NUM_HSTIMER0) { return res; }
	}

	return LEDC_TIMER_BIT_MAX;
}

esp_err_t PWM_start(pwm_handle_t _pwm, uint32_t freq, int dutyRatio, uint32_t count, int outputPin, FUNC_pwmIsrCallback callback, void *cookie) {
	pwm_info_t *pwm = _pwm;

	// 在指定频率下，获取最佳的 duty 分辨率
	ledc_timer_bit_t duty_resolution = ledc_get_best_duty_resolution(pwm, freq);
	if(duty_resolution >= LEDC_TIMER_BIT_MAX) { return ESP_ERR_INVALID_ARG; }

    /*
     * Prepare and set configuration of timers
     * that will be used by LED Controller
     */
    ledc_timer_config_t ledc_timer = {
        .duty_resolution = duty_resolution,		// resolution of PWM duty
        .freq_hz = freq,                        // frequency of PWM signal
        .speed_mode = LEDC_HIGH_SPEED_MODE,     // timer mode
        .timer_num = pwm->timer          		// timer index
    };
    // Set configuration of timer0 for high speed channels
    RET_ON_ERROR( ledc_timer_config(&ledc_timer) );

    /*
     * Prepare individual configuration
     * for each channel of LED Controller
     * by selecting:
     * - controller's channel number
     * - output duty cycle, set initially to 0
     * - GPIO number where LED is connected to
     * - speed mode, either high or low
     * - timer servicing selected channel
     *   Note: if different channels use one timer,
     *         then frequency and bit_num of these channels
     *         will be the same
     */
    ledc_channel_config_t config = {
        .channel    = pwm->channel,
        .duty       = PWM_COUNT_PER_DUTY(duty_resolution, dutyRatio),
        .gpio_num   = ABS(outputPin),
        .speed_mode = LEDC_HIGH_SPEED_MODE,
        .hpoint     = 0,
        .timer_sel  = pwm->timer,
		.intr_type  = LEDC_INTR_DISABLE,
    };
    RET_ON_ERROR( ledc_channel_config(&config) );

    // 挂接中断处理函数
    if(!pwm_isr_handle) {
		//ledc_isr_register(ledc_pwm_isr, &stepInfo, ESP_INTR_FLAG_IRAM, NULL);
		//esp_intr_alloc_intrstatus(ETS_LEDC_INTR_SOURCE, ESP_INTR_FLAG_IRAM, (uint32_t)(&LEDC.int_st.val), pwm->timer, ledc_pwm_isr, pwm, &pwm_isr_handle);
    	RET_ON_ERROR( esp_intr_alloc(ETS_LEDC_INTR_SOURCE, ESP_INTR_FLAG_IRAM, ledc_pwm_isr, NULL, &pwm_isr_handle) );
    }

    pwm->callback = callback; pwm->cookie = cookie;
    pwm->total = count;
    pwm->gpio = outputPin;

    // 开始计数
    PWM_restart(_pwm);

    return ESP_OK;
}

void PWM_restart(pwm_handle_t _pwm) {
	pwm_info_t *pwm = _pwm;

	// 重新开始计数
    pwm->remain = pwm->total;
    // 继续
    PWM_continue(_pwm);
}

void PWM_continue(pwm_handle_t _pwm) {
	pwm_info_t *pwm = _pwm;

    // 时钟开始
    LEDC.channel_group[LEDC_HIGH_SPEED_MODE].channel[pwm->channel].conf0.sig_out_en = (pwm->gpio >= 0);
    LEDC.channel_group[LEDC_HIGH_SPEED_MODE].channel[pwm->channel].conf1.duty_start = 1;
    //LEDC.timer_group[LEDC_HIGH_SPEED_MODE].timer[pwm->timer].conf.pause = 0;

    // 便能对应的 TIMER 计数溢出中断
    uint32_t int_ena_val = LEDC.int_ena.val;
    LEDC.int_ena.val = int_ena_val | BIT(LEDC_HSTIMER0_OVF_INT_ENA_S + pwm->timer);
}

void PWM_stop(pwm_handle_t _pwm) {
	pwm_info_t *pwm = _pwm;

    // 禁用对应的 TIMER 计数溢出中断
    uint32_t int_ena_val = LEDC.int_ena.val;
    LEDC.int_ena.val = int_ena_val & ~BIT(LEDC_HSTIMER0_OVF_INT_ENA_S + pwm->timer);

    // 时钟停止
    //ledc_stop(LEDC_HIGH_SPEED_MODE, pwm->channel, 0);
    LEDC.channel_group[LEDC_HIGH_SPEED_MODE].channel[pwm->channel].conf0.idle_lv = RESET;
    LEDC.channel_group[LEDC_HIGH_SPEED_MODE].channel[pwm->channel].conf1.duty_start = 0;
    LEDC.channel_group[LEDC_HIGH_SPEED_MODE].channel[pwm->channel].conf0.sig_out_en = 0;
    //LEDC.timer_group[LEDC_HIGH_SPEED_MODE].timer[pwm->timer].conf.pause = 1;

    // GPIO引脚恢复成普通 GPIO 输出
    //GPIO_Config(GPIO_NUM_4, GPIO_MODE_OUTPUT, GPIO_PULLUP_ENABLE, GPIO_PULLDOWN_ENABLE, GPIO_INTR_DISABLE);
    //gpio_set_direction(pwm->gpio, GPIO_MODE_OUTPUT);
}
